package com.tianji.remark.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.constants.RedisConstants;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.dto.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author rui
 * @since 2024-07-25
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LikedRecordServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {
    private final RabbitMqHelper rabbitMqHelper;
    private final StringRedisTemplate redisTemplate;
    @Override
    public void addLikedRecord(LikeRecordFormDTO dto) {
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.根据是否点赞字段判断
        boolean isLiked = dto.getLiked() ? liked(userId,dto) : unLiked(userId,dto);//为true代表处理成功
        //如果处理失败，则说明发生参数错误
        if(!isLiked){
            return;
        }
        //3.把点赞总数缓存到redis
        String BizIdKey = RedisConstants.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        Long size = redisTemplate.opsForSet().size(BizIdKey);
        if(size == null){
            return;
        }
        String key = RedisConstants.LIKES_TIMES_KEY_PREFIX + dto.getBizType();
        redisTemplate.opsForZSet().add(key,dto.getBizId().toString(),size);

        /*//3.查询点赞数，点赞或者取消点赞需要回答或者评论中修改点赞数
        Integer likedTimes = lambdaQuery()
                .eq(LikedRecord::getBizId, dto.getBizId())
                .count();
        LikedTimesDTO likedTimesDTO = new LikedTimesDTO();
        likedTimesDTO.setLikedTimes(likedTimes);
        likedTimesDTO.setBizId(dto.getBizId());
        //3.1 发送mq消息
        log.debug("发送点赞mq消息，内容为{}",likedTimesDTO);
        String routingKey = StrUtil.format(MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE, dto.getBizType());
        rabbitMqHelper.send(
                MqConstants.Exchange.LIKE_RECORD_EXCHANGE,
                routingKey,
                likedTimesDTO
        );*/
    }

    @Override
    public Set<Long> likedQueryByBizIds(List<Long> bizIds) {
        //用redis进行优化
        /**
         *  不要在一次批处理中传输太多命令，否则单次命令占用带宽过多，会导致网络阻塞
         * Spring提供的RedisTemplate也具备pipeline功能，最终批量查询点赞状态功能实现如下：
         */
        // 1.获取登录用户id
        Long userId = UserContext.getUser();
        // 2.查询点赞状态
        List<Object> objects = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection src = (StringRedisConnection) connection;
            for (Long bizId : bizIds) {
                String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + bizId;
                src.sIsMember(key, userId.toString());
            }
            return null;
        });
        // 3.返回结果
        return IntStream.range(0, objects.size()) // 创建从0到集合size的流
                .filter(i -> (boolean) objects.get(i)) // 遍历每个元素，保留结果为true的角标i
                .mapToObj(bizIds::get)// 用角标i取bizIds中的对应数据，就是点赞过的id
                .collect(Collectors.toSet());// 收集
        /**
         * 需要多次调用SISMEMBER命令，也就需要向Redis多次发起网络请求，给网络带宽带来非常大的压力，影响业务性能
         */
//        Long userId = UserContext.getUser();
//        Set<Long> result = new HashSet<>();
//        for (Long bizId : bizIds) {
//            String BizIdKey = RedisConstants.LIKE_BIZ_KEY_PREFIX + bizId;
//            //判断用户是否点过赞
//            Boolean member = redisTemplate.opsForSet().isMember(BizIdKey, userId.toString());
//            if(Boolean.TRUE.equals(member)){
//                result.add(bizId);
//            }
//        }
        //return result;
    }

    @Override
    public void readLikedTimesAndSendMessage(String bizType, int maxBizSize) {
        //读取缓存消息的总数
        String key = RedisConstants.LIKES_TIMES_KEY_PREFIX + bizType;
        //1.通过定时任务每隔20s处理 30条数据 ,没有数据直接返回
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(key, maxBizSize);
        if(CollUtil.isEmpty(typedTuples)){
            return;
        }
        List<LikedTimesDTO> list = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            Double likedTimes = typedTuple.getScore();
            String bizId = typedTuple.getValue();
            LikedTimesDTO likedTimesDTO = new LikedTimesDTO();
            if (likedTimes == null || bizId == null) {
                continue;
            }
            likedTimesDTO.setBizId(Long.valueOf(bizId));
            likedTimesDTO.setLikedTimes(likedTimes.intValue());
            list.add(likedTimesDTO);
        }

        if(CollUtil.isNotEmpty(list)){
            // 3.批量修改点赞数量
            rabbitMqHelper.send(
                    LIKE_RECORD_EXCHANGE,
                    StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, bizType),
                    list);
        }
    }

    private boolean unLiked(Long userId, LikeRecordFormDTO dto) {
        //处理取消点赞逻辑,redis 优化
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        Long remove = redisTemplate.opsForSet().remove(key, userId.toString());
        return remove != null && remove > 0;

        /*//1. 查询是否有该业务
        LikedRecord likedRecord = lambdaQuery().eq(LikedRecord::getUserId, userId)
                .eq(LikedRecord::getBizId, dto.getBizId()).one();
        if(likedRecord == null){
            //1.1说明没有点过赞，无法取消点赞
            return false;
        }
        //2.取消点赞
        return removeById(likedRecord.getId());*/
    }


    private boolean liked(Long userId, LikeRecordFormDTO dto) {
        //处理点赞逻辑,用redis方案进行优化
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        Long result = redisTemplate.opsForSet().add(key, userId.toString());
        return result != null && result > 0;

        //1. 查询是否有该业务
        /*LikedRecord likedRecord = lambdaQuery().eq(LikedRecord::getUserId, userId)
                .eq(LikedRecord::getBizId, dto.getBizId()).one();
        if(likedRecord != null){
            //1.1说明已经点过赞，无法重复点赞
            return false;
        }
        //2.进行点赞操作
        LikedRecord record = new LikedRecord();
        record.setBizId(dto.getBizId());
        record.setUserId(userId);
        record.setBizType(dto.getBizType());
        boolean save = save(record);
        return save;*/
    }
}
